//ticketclerk.cc

#include "cineplex.h"

TicketClerk::TicketClerk(char* name, Line* line)
{
	myName = name;
	msgList = new MsgList();
	myThread = new Thread(myName);
	msgLock = new Lock(myName);
	sleepCon = new Condition(myName);
	myLine=line;
	status="Idle";
	moneyCollected = new float;
	*moneyCollected=0.0f;
	TICKET_PRICE=12.00f;
	myLine->myClerk =(void *) this;
	myLine->myType=1;
	onBreak=false;
}

TicketClerk::~TicketClerk()
{

}

void
TicketClerk::Start()
{
	printf("Starting TicketClerk %s...\n", myName);
	if(!getCineplex()->testPattern)
	{
		if(getCineplex()->runSchedulers) Scheduler();
	}else{
		if(getCineplex()->testPattern == 3) CT3();
		else if(getCineplex()->testPattern == 4) CT4();
	}
}

void
TicketClerk::SendMessage(char* name, int argc, ...)
{
	msgLock->Acquire();
	IterList* args = new IterList();
	va_list vali;
	va_start(vali, argc);
	
	//printf("%s rec msg \"%s\"\n",myName,name);
	
	for ( int i = 0; i < argc; i++ )        
    {
        args->Append(va_arg ( vali, void* )); 
    }
	va_end(vali);
	
	msgList->Append(name, args);
	sleepCon->Signal(msgLock);
	
	msgLock->Release();
}

void
TicketClerk::SendMessage(char* name, IterList* args)
{
	msgLock->Acquire();
	
	msgList->Append(name, args);
	sleepCon->Signal(msgLock);
	
	msgLock->Release();
}

void TicketClerk::CT3()
{
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	getCineplex()->testAgentsLeft = getCineplex()->testAgentsLeft - 1;
}

void TicketClerk::CT4()
{
	currentThread->Yield();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
	currentThread->Yield();
	Scheduler();
}

void
TicketClerk::Scheduler()
{
	bool runScheduler = getCineplex()->runSchedulers;
	do
	{
		msgLock->Acquire();
		IterList* msg;
		if(status=="TalkingToManager")
		{
			//Since the message had no arguments, I don't need to store it, just see if it is returned
			if(msgList->Find("Okay"))
			{
				ContinueOn();
				msgLock->Release();
				continue;
			}
			
			else if(msgList->Find("GoOnBreak"))
			{
				GoOnBreak();
				msgLock->Release();
				continue;
			}
			else if(msgList->Find("ComeOffBreak"))
			{
				ComeOffBreak();
				msgLock->Release();
				continue;
			}
		}
		else if(status=="WaitingForCust")
		{
			//I need to use the arguments, so I store the iterList
			msg=msgList->Find("OrderTix");
			if(msg)
			{
				AskForPayment(msg->Get(0),msg->GetInt(1));
				msgLock->Release();
				continue;
			}
		}
		else if(status=="WaitingForPay")
		{
			msg=msgList->Find("HereIsTixMoney");
			if(msg)
			{
				//moneyCollected += msg->GetFloat(1);
				GiveTickets(msg->Get(0),msg->GetFloat(1));
				msgLock->Release();
				continue;
			}
		}
		else if(status=="OnBreak")
		{
			msg=msgList->Find("WhatIsStatus");
			if(msg)
			{
				TellStatus(msg->Get(0));
				msgLock->Release();
				continue;
			}
			msg=msgList->Find("GiveMeMoney");
			if(msg)
			{
				GiveManagerMoney(msg->Get(0));
				msgLock->Release();
				continue;
			}
		}
		else if(status=="Idle")
		{
			msg=msgList->Find("WhatIsStatus");
			if(msg)
			{
				TellStatus(msg->Get(0));
				msgLock->Release();
				continue;
			}
			msg=msgList->Find("GiveMeMoney");
			if(msg)
			{
				GiveManagerMoney(msg->Get(0));
				msgLock->Release();
				continue;
			}
			else if(CallNextInLine())
			{
				msgLock->Release();
				continue;
			}
		}
		msgLock->Release();
		sleepCon->Wait(msgLock); //don't want them sleeping if there's a test running
	} while(runScheduler);
} 

//Gets the next customer in his ticket line.  Returns true if there is one, false otherwise
bool
TicketClerk::CallNextInLine()
{
	getCineplex()->tixLinesLock->Acquire();
	Customer* cust=(Customer *)myLine->customers->Pop(0);
	getCineplex()->tixLinesLock->Release();
	if(cust)
	{	
		printf("TicketClerk %s has a line length %i\n", myName, myLine->getSize()+1);//
		cust->SendMessage("ComeBuyTix",1,this);
		status="WaitingForCust";
		return true;
	}
	printf("TicketClerk %s has no one in line.  I am available for a customer\n", myName);
	return false;
}

//Asks the Customer for payment based ont he size of the group
void
TicketClerk::AskForPayment(void* custPtr, int size)
{
	Customer* cust=(Customer *)custPtr;
	money=TICKET_PRICE*size;
	printf("TicketClerk %s has an order for %i and the cost is %.2f\n", myName,size,money);
	cust->SendMessage("PayMeForTix",2,this,&money);
	status="WaitingForPay";	
}

//Gives the Customer the specified number of tickets and adds the money to his current take
void
TicketClerk::GiveTickets(void* custPtr, float payment)
{
	Customer* cust=(Customer *)custPtr;
	Ticket* pool = getCineplex()->ticketPool;
	Ticket* tickets = &(pool[getCineplex()->tixPassedOut]);
	getCineplex()->tixPassedOut+=groupSize;
	cust->SendMessage("HereAreTickets",2,tickets,&groupSize);
	*moneyCollected+=payment;
	status="Idle";
}

void
TicketClerk::GiveManagerMoney(void* mngrPtr)
{
	Manager* manager=(Manager *)mngrPtr;
	manager->SendMessage("HereIsMoney",2,this,moneyCollected);
	moneyCollected = new float;
	*moneyCollected=0.0f;
	
}

//Tells the manager how many people are in his line.  If he is on break, he returns -1
void
TicketClerk::TellStatus(void* mngrPtr)
{
	Manager* manager=(Manager *)mngrPtr;
	if(status=="OnBreak")
	{
		lineLength=-1;
		onBreak=true;
	}
	else
	{
		lineLength=myLine->getSize();
		onBreak=false;
	}
	
	manager->SendMessage("NumInLine",2,this,&lineLength);
	status="TalkingToManager";
}

//The manager doesn't tell the clerk to change break status, so he returns to the previous state
void
TicketClerk::ContinueOn()
{
	if(onBreak)
	{
		status="OnBreak";
	}
	else
	{
		status="Idle";
	}
}

//Clerks goes on break
void
TicketClerk::GoOnBreak()
{
	printf("TicketClerk %s is going on break\n", myName);
	myLine->isOpen=0;
	myLine->Clear();
	status="OnBreak";
}

void
TicketClerk::ComeOffBreak()
{
	printf("TicketClerk %s is coming off break\n", myName);
	myLine->isOpen=1;
	status="Idle";
}
